/* SPDX-License-Identifier: BSD-3-Clause
 * Copyright(c) 2010-2014 Intel Corporation
 */

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <inttypes.h>
#include <sys/types.h>
#include <string.h>
#include <sys/queue.h>
#include <stdarg.h>
#include <errno.h>
#include <getopt.h>
#include <arpa/inet.h>
#include <inttypes.h>

#include <rte_common.h>
#include <rte_byteorder.h>
#include <rte_log.h>
#include <rte_memory.h>
#include <rte_memcpy.h>
#include <rte_eal.h>
#include <rte_per_lcore.h>
#include <rte_launch.h>
#include <rte_cycles.h>
#include <rte_prefetch.h>
#include <rte_branch_prediction.h>
#include <rte_interrupts.h>
#include <rte_pci.h>
#include <rte_random.h>
#include <rte_debug.h>
#include <rte_ether.h>
#include <rte_ethdev.h>
#include <rte_ring.h>
#include <rte_mempool.h>
#include <rte_mbuf.h>
#include <rte_ip.h>
#include <rte_tcp.h>
#include <rte_lpm.h>
#include <rte_lpm6.h>
#include <rte_malloc.h>

#include "main.h"


static void
l2fwd_mac_updating(struct rte_mbuf *m, unsigned dst_port)
{
	struct rte_ether_hdr *eth;

	// void *tmp;

	eth = rte_pktmbuf_mtod(m, struct rte_ether_hdr *);

	/* 02:00:00:00:00:xx */
	// tmp = &eth->dst_addr.addr_bytes[0];
	//*((uint64_t *)tmp) = 0x000000000002 + ((uint64_t)dest_portid << 40);
	struct rte_ether_addr dst_addr = eth->dst_addr;
	rte_ether_addr_copy(&eth->src_addr, &eth->dst_addr);
	/* src addr */
	eth->src_addr = dst_addr;
	// rte_ether_addr_copy(&l2fwd_ports_eth_addr[dst_port], &eth->src_addr);
}

static void
l2fwd_ip_updating(struct rte_mbuf *m)
{
	struct rte_ipv4_hdr *hdr;
	uint32_t tmp_addr;

	hdr = rte_pktmbuf_mtod_offset(m, struct rte_ipv4_hdr *, sizeof(struct rte_ether_hdr));

	tmp_addr = hdr->dst_addr;
	hdr->dst_addr = hdr->src_addr;
	hdr->src_addr = tmp_addr;
}

static void
l2fwd_tcp_updating(struct rte_mbuf *m)
{
	struct rte_tcp_hdr *hdr;
	uint16_t tmp_port;
	hdr = rte_pktmbuf_mtod_offset(m, struct rte_tcp_hdr *, sizeof(struct rte_ether_hdr) + sizeof(struct rte_ipv4_hdr));

	tmp_port = hdr->dst_port;
	hdr->dst_port = hdr->src_port;
	hdr->src_port = tmp_port;
	hdr->recv_ack = rte_cpu_to_be_32(1);
}

static bool is_data_pkt(struct rte_mbuf *m)
{
	struct rte_ether_hdr *eth_hdr;
	struct rte_ipv4_hdr *ipv4_hdr;
	struct rte_tcp_hdr *tcp_hdr;
	eth_hdr = rte_pktmbuf_mtod(app.mbuf_rx.array[0], struct rte_ether_hdr *); // 解析第一个数据包
	ipv4_hdr = (struct rte_ipv4_hdr *)(eth_hdr + 1);
	tcp_hdr = (struct rte_tcp_hdr *)(ipv4_hdr + 1);

	// struct in_addr src_ip;
	// src_ip.s_addr = ipv4_hdr->src_addr;
	rte_be16_t pkt_id = rte_be_to_cpu_16(ipv4_hdr->packet_id);

	if (pkt_id == 5462)
	{
		return 1;
	}
	return 0;
}

// 返回当前交换机内部的队列长度大小
static uint32_t get_queue_pkts()
{
	return app.queue_pkts;
}

// 返回当前瞬时链路利用率大小
static uint32_t get_link_util()
{
	// 用交换机出端口的瞬时转发速率除以出端口的额定带宽(10Gbps)

	return 0;
}

// 判断状态，例如，如果交换机队列长度大于50个包，那么进行队列长度编码，否则进行链路利用率的编码
static uint32_t encoding()
{
	if (get_queue_pkts() > 50)
	{
		// 修改待发的m_buf[array]的ECN标记字段, 根据get_queue_pkts()的值进行编码，ECN11为1，ECN01为0，注意分流
	}
	else
	{
		// 修改待发的m_buf[array]的ECN标记字段, 根据get_queue_pkts()的值进行编码，ECN10为1，ECN01为0，注意分流
	}
}

// 注意，此函数在交换机中也不应该出现，由于之前条件的限制，所以在这个地方交换机当receiver用，真实物理环境下不应该用此函数
/**
 * @param tos 设置ECN用
 * @param m 接收到的数据报文，我们需要根据对应的数据报文转发ack
 * @param thoughput 当前交换机的转发吞吐，发送端根据该消息进行动态调速
 */
static struct rte_mbuf *create_ack_pkt(uint8_t tos, struct rte_mbuf *m, uint64_t rate)
{
	// 构造数据域
	struct rte_mbuf *created_pkt;
	struct rte_ether_hdr *eth_hdr;
	struct rte_ipv4_hdr *ipv4_hdr;
	struct rte_icmp_hdr *icmp_hdr;
	uint64_t *payload;

	struct rte_ipv4_hdr *data_ip_hdr;
	data_ip_hdr = rte_pktmbuf_mtod_offset(m, struct rte_ipv4_hdr *, sizeof(struct rte_ether_hdr)); // 解析拿到接收到的数据的头部信息

	size_t pkt_size;

	created_pkt = rte_pktmbuf_alloc(app.pool);
	if (created_pkt == NULL)
	{
		RTE_LOG(WARNING, USER1, "Failed to allocate mbuf for ICMP message\n");
		return NULL;
	}

	pkt_size = sizeof(struct rte_ether_hdr) + sizeof(struct rte_ipv4_hdr) + sizeof(struct rte_icmp_hdr) + sizeof(uint64_t);
	created_pkt->data_len = pkt_size;
	created_pkt->pkt_len = pkt_size;

	/* Ethernet */
	eth_hdr = rte_pktmbuf_mtod(created_pkt, struct rte_ether_hdr *);
	rte_eth_macaddr_get(1, &eth_hdr->src_addr); // 将交换机的端口1的mac地址做为源地址
	memset(&eth_hdr->dst_addr, 0xFF, RTE_ETHER_ADDR_LEN);
	eth_hdr->ether_type = rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV4);

	/* IPv4 */
	ipv4_hdr = (struct rte_ipv4_hdr *)((char *)eth_hdr +
									   sizeof(struct rte_ether_hdr));
	ipv4_hdr->version_ihl = IP_VHL_DEF;
	ipv4_hdr->next_proto_id = IPPROTO_ICMP;
	ipv4_hdr->total_length = rte_cpu_to_be_16(
		sizeof(struct rte_ipv4_hdr) + sizeof(struct rte_icmp_hdr) +
		sizeof(uint64_t));
	ipv4_hdr->time_to_live = IP_DEFTTL;
	ipv4_hdr->src_addr = rte_cpu_to_be_32(rte_be_to_cpu_32(data_ip_hdr->dst_addr));
	ipv4_hdr->dst_addr = rte_cpu_to_be_32(rte_be_to_cpu_32(data_ip_hdr->src_addr));

	// ipv4_hdr->src_addr = rte_cpu_to_be_32(0xC0A83FDD);//192.168.63.221
	// ipv4_hdr->dst_addr = rte_cpu_to_be_32(0xC0A800DA); // 192.168.0.218
	ipv4_hdr->hdr_checksum = 0;

	/* ICMP */
	icmp_hdr = (struct rte_icmp_hdr *)((char *)ipv4_hdr +
									   sizeof(struct rte_ipv4_hdr));
	icmp_hdr->icmp_type = IP_ICMP_PEE;
	icmp_hdr->icmp_code = 0;

	/* Payload */
	payload = (uint64_t *)((char *)icmp_hdr + sizeof(struct rte_icmp_hdr));
	*payload = rate;

	return created_pkt;
}

// 三个参数分别是上一次的时间，余量，包大小，注意，在真实物理环境不应该用该方法
static void drain_pkts(uint64_t *prev_tsc, uint32_t *left_bits, uint32_t *q_len, const uint64_t sec_tsc, uint32_t pkt_len)
{
	uint64_t cur_tsc, diff_tsc;
	cur_tsc = rte_rdtsc();															   // 当前时钟数
	diff_tsc = cur_tsc - *prev_tsc;													   // 已经过了多少个时钟数
	uint32_t should_drain_bits = (diff_tsc * SWITCH_BANDWIDTH * 1e6 / sec_tsc) + *left_bits; // 在这段时间间隔内应该传输这么多字节

	uint16_t drain_nums = (should_drain_bits / 8) / pkt_len; // 应该排出的数据包的个数,pkt_len是以字节为单位的
	if (*q_len > drain_nums)
	{
		*q_len = *q_len - drain_nums;
	}
	else
	{
		*q_len = 0;
	}
	*left_bits = should_drain_bits - drain_nums * pkt_len * 8;

	printf("drain %d pkts!,q_len:%d\n", drain_nums, *q_len);

	*prev_tsc = cur_tsc;
}

// 每秒计算一次吞吐，供后台打印输出信息
static void update_throughput(const uint32_t port, const uint64_t sec_tsc, struct rte_mbuf **array, uint16_t n_mbufs)
{
	double interval, cur_time;
	uint64_t cur_tsc;
	struct tp_meter *tp_meter;

	cur_tsc = rte_rdtsc();
	tp_meter = &app.e2pcc_param.tp_in;
	interval = (cur_tsc - tp_meter->intvl_start_time) / (double)sec_tsc;

	for (int i = 0; i < n_mbufs; i++)
	{
		if (!is_data_pkt(array[i]))
		{
			continue;
		}
		if (100 <= tp_meter->intvl_num_pkts || sec_tsc <= cur_tsc - tp_meter->intvl_start_time) // 每发100个包记录一次或者当没有100个包，那么每隔1秒记录一次
		{
			cur_time = (cur_tsc - app.e2pcc_param.start_time) / (double)sec_tsc; // 当前时间
			app.e2pcc_param.flow_num = (app.e2pcc_param.switch_state[0].tp_in.intvl_num_pkts > 0 && app.e2pcc_param.switch_state[1].tp_in.intvl_num_pkts > 0) ? 2 :1;
			if (sec_tsc <= cur_tsc - app.e2pcc_param.last_print_time) // 供后台打印信息用，可不用管
			{
				printf("thoughtput: %.2fs %.2fMbps %lu %d,port0:%lu,port1:%lu,flows:%d\n", cur_time, (tp_meter->intvl_num_bits / interval) / 1e6, tp_meter->intvl_num_pkts, array[i]->pkt_len, app.e2pcc_param.switch_state[0].tp_in.intvl_num_pkts, app.e2pcc_param.switch_state[1].tp_in.intvl_num_pkts, app.e2pcc_param.flow_num); // 分别是时间(s)，吞吐(Gbps), 包的个数，包的大小,端口0的包数，端口1的包数，流数目
				app.e2pcc_param.last_print_time = cur_tsc;
			}

			app.e2pcc_param.input_thoughput = (uint64_t)(1e3 * tp_meter->intvl_num_bits / interval); // 注意，在接收端收到数据时，应该除以1e9,得到浮点数。
			tp_meter->intvl_start_time = cur_tsc;
			tp_meter->intvl_num_bits = 0;
			tp_meter->intvl_num_pkts = 0;
			app.e2pcc_param.switch_state[0].tp_in.intvl_num_pkts = 0; // 因为我们已知交换机就两个入端口，所以这段就写死了
			app.e2pcc_param.switch_state[1].tp_in.intvl_num_pkts = 0;
		}
		tp_meter->intvl_num_bits += array[i]->pkt_len * 8;
		tp_meter->intvl_num_pkts++;

		app.e2pcc_param.switch_state[port].tp_in.intvl_num_pkts++; // 记录每个端口
	}
}

static void print_ack_info(struct rte_mbuf *m)
{
	struct rte_ether_hdr *eth_hdr;
	struct rte_ipv4_hdr *ipv4_hdr;
	struct rte_tcp_hdr *tcp_hdr;
	eth_hdr = rte_pktmbuf_mtod(app.mbuf_rx.array[0], struct rte_ether_hdr *); // 解析第一个数据包
	ipv4_hdr = (struct rte_ipv4_hdr *)(eth_hdr + 1);
	tcp_hdr = (struct rte_tcp_hdr *)(ipv4_hdr + 1);

	struct in_addr src_ip;
	src_ip.s_addr = ipv4_hdr->src_addr;

	// struct in_addr dst_ip;
	// dst_ip.s_addr = ipv4_hdr->dst_addr;

	printf("src ip:%s, ", inet_ntoa(src_ip));
	printf("dst ip:%s, tos:%d, pkt_len:%d\n", inet_ntoa(*(struct in_addr *)(&ipv4_hdr->dst_addr)), ipv4_hdr->type_of_service, app.mbuf_rx.array[0]->pkt_len);
}

static void print_pkt_info(uint32_t *totalPkts, const uint16_t *n_mbufs)
{
	struct rte_ether_hdr *eth_hdr;
	struct rte_ipv4_hdr *ipv4_hdr;
	struct rte_tcp_hdr *tcp_hdr;
	uint64_t sec_tsc = rte_get_tsc_hz();
	eth_hdr = rte_pktmbuf_mtod(app.mbuf_rx.array[0], struct rte_ether_hdr *); // 解析第一个数据包
	ipv4_hdr = (struct rte_ipv4_hdr *)(eth_hdr + 1);
	tcp_hdr = (struct rte_tcp_hdr *)(ipv4_hdr + 1);

	int hours, minutes, seconds;
	// `time_t` 是算术时间类型
	time_t now;
	time(&now);
	struct tm *local = localtime(&now);

	hours = local->tm_hour;	 // 获取自午夜以来的小时数 (0-23)
	minutes = local->tm_min; // 获取小时后经过的分钟数 (0-59)
	seconds = local->tm_sec; // 获取一分钟后经过的秒数 (0-59)

	(*totalPkts) += (*n_mbufs);

	struct in_addr src_ip;
	src_ip.s_addr = ipv4_hdr->src_addr;

	if (rte_be_to_cpu_32(ipv4_hdr->dst_addr) == 0xC0A80A14) // 如果是发送给接收端的包。
	{
		// pass
	}

	// printf("seq num:%d\n",rte_be_to_cpu_32(tcp_hdr->sent_seq));
	// 获取当前时间
	// `time()` 以 `time_t` 值返回系统的当前时间
	printf("%02d:%02d:%02d:", hours, minutes, seconds);
	printf("having received %d pkts!,total:%d,src ip:%s,", *n_mbufs, *totalPkts, inet_ntoa(src_ip));
	printf("dst ip:%s, tos:%d, pkt_len:%d\n", inet_ntoa(*(struct in_addr *)(&ipv4_hdr->dst_addr)), ipv4_hdr->type_of_service, app.mbuf_rx.array[0]->pkt_len);
}

// 进行数据包的转发
static void simple_forward(struct rte_mbuf *m, unsigned portid)
{
	int sent;
}

void app_main_loop_rx(void)
{
	uint32_t i;
	int ret;
	const uint64_t sec_tsc = rte_get_tsc_hz(); // 获取主机1s内的时钟频率
	uint32_t totalPkts = 0;

	RTE_LOG(INFO, USER1, "Core %u is doing RX\n", rte_lcore_id());

	uint64_t prev_tsc = 0;

	uint32_t left_bits = 0;

	uint32_t queue_pkts = 0; // 数据包的个数

	for (i = 0;; i = (i + 1) % 2)
	{
		uint16_t n_mbufs, n_batch, n_read, n_write;

		n_mbufs = app.mbuf_rx.n_mbufs;
		if (n_mbufs < 0)
		{
			n_mbufs = 0;
		}
		n_batch = APP_MBUF_ARRAY_SIZE - n_mbufs;
		if (app.burst_size_rx_read < n_batch)
			n_batch = app.burst_size_rx_read;

		if (n_batch != 0)
		{
			n_read = rte_eth_rx_burst(i, 0, &app.mbuf_rx.array[n_mbufs], n_batch); // 从第一个网卡接受数据
			app.queue_pkts += n_read;											   // 读取了几个包queue_pkts就增加几个，是包的个数，并不是字节数

			n_mbufs += n_read;
		}

		// 注意！！！此处的drain_pkts是模拟交换机的转发包功能，在真实环境跑该函数应该去掉。
		//  drain_pkts(&prev_tsc, &left_bits, &app.queue_pkts, sec_tsc, 1454); //因为已经知道了收到的数据包的大小是1454，所以直接写死了。注意，此处是模拟出包过程

		if (n_read == 0)
			continue;

		update_throughput(i, sec_tsc, app.mbuf_rx.array, n_read); // 计算网卡的总吞吐。
		//  print_pkt_info(&totalPkts, &n_read);
		for (uint32_t j = 0; j < n_read; j++)
		{
			struct rte_mbuf *ack_mbuf;
			if(app.e2pcc_param.flow_num>0 && is_data_pkt(app.mbuf_rx.array[j])){
				ack_mbuf = create_ack_pkt(0x03, app.mbuf_rx.array[j], (uint64_t)(SWITCH_BANDWIDTH/app.e2pcc_param.flow_num)); // 生成ack数据包
				rte_eth_tx_burst(i, 0, &ack_mbuf, 1);													// 依次进行转发ack数据包，从原端口转发
			}
		}

		// 释放内存
		for (uint16_t buf = 0; buf < n_mbufs; buf++)
		{
			rte_pktmbuf_free(app.mbuf_rx.array[buf]);
			app.mbuf_rx.n_mbufs = 0;
		}
	}
}

void app_main_loop_worker(void)
{
	struct app_mbuf_array *worker_mbuf;
	uint32_t i;

	RTE_LOG(INFO, USER1, "Core %u is doing work (no pipeline)\n",
			rte_lcore_id());

	worker_mbuf = rte_malloc_socket(NULL, sizeof(struct app_mbuf_array),
									RTE_CACHE_LINE_SIZE, rte_socket_id());
	if (worker_mbuf == NULL)
		rte_panic("Worker thread: cannot allocate buffer space\n");

	for (i = 0;; i = ((i + 1) & (app.n_ports - 1)))
	{
		int ret;

		ret = rte_ring_sc_dequeue_bulk( // 从队列里出队，app.rings_rx[i]出队的数据放到worker_mbuf->array里面
			app.rings_rx[i],
			(void **)worker_mbuf->array,
			app.burst_size_worker_read,
			NULL);

		if (ret == 0)
			continue;

		do
		{
			ret = rte_ring_sp_enqueue_bulk( // 再从worker_mbuf->array转存到tx的ring队列里面
				app.rings_tx[i ^ 1],
				(void **)worker_mbuf->array,
				app.burst_size_worker_write,
				NULL);
		} while (ret == 0);
	}
}

void app_main_loop_tx(void)
{
	uint32_t i;

	RTE_LOG(INFO, USER1, "Core %u is doing TX\n", rte_lcore_id());

	// struct rte_mbuf *mbuf;
	// // mbuf = create_ack_pkt(0x3,); //

	// for (i = 0;;)
	// {
	// 	uint16_t n_mbufs, n_pkts;
	// 	int ret;

	// 	n_pkts = rte_eth_tx_burst(1, 0, &mbuf, 1);

	// 	app.queue_pkts -= n_pkts; // 向外发送了几个包当前队列就减少几个。

	// 	if (n_pkts < n_mbufs)
	// 	{
	// 		uint16_t k;

	// 		for (k = n_pkts; k < n_mbufs; k++)
	// 		{
	// 			struct rte_mbuf *pkt_to_free;

	// 			pkt_to_free = app.mbuf_tx[i].array[k];
	// 			rte_pktmbuf_free(pkt_to_free);
	// 		}
	// 	}

	// 	app.mbuf_tx[i].n_mbufs = 0;
	// }
}
